//
//  SignalSlot.cpp
//  smallworld
//
//  Created by 周俊 on 14/11/25.
//
//

#include "SignalSlot.h"


TSP_NS_START
/*------------------------------------------------*/

Slot::Slot()
{
}

Slot::~Slot()
{
}

Slot* Slot::createSlot(const SLOT_FUNCTION &slot_function, int priority, bool repeat)
{
    Slot* ret = new Slot();
    ret->m_enable = true;
    ret->m_priority = priority;
    ret->m_refCount = 0;
    ret->m_repeat = repeat;
    ret->m_slotFunction = slot_function;
    return ret;
}

void Slot::retain()
{
    m_refCount += 1;
}

void Slot::release()
{
    m_enable = false;
    m_refCount -= 1;
    if (m_refCount <= 0 )
    {
        delete this;
    }
}

void Slot::connect(Signal *signal)
{
    m_refCount += 1;
}

void Slot::disconnect(Signal *signal)
{
    m_refCount -= 1;
    if (m_refCount <= 0 )
    {
        delete this;
    }
}

bool Slot::run(void *param)
{
    bool ret = false;
    if (m_enable)
    {
        m_slotFunction(param);
        ret = m_repeat;
        m_enable = m_repeat;
    }

    //return false to make signal disconnect the slot object.
    //return true to make signal keeping it.
    return ret;
}

/*------------------------------------------------*/
bool slotPriorityComparisonLess(Slot* a, Slot* b)
{
    return a->getPriority() <= b->getPriority();
}

Signal::Signal()
{
    m_connectedSlots = new std::list<Slot*>();
}

Signal::~Signal()
{
    for (Slot* slot: *m_connectedSlots)
    {
        slot->disconnect(this);
    }
    m_connectedSlots->clear();
    TSP_SAFE_DELETE(m_connectedSlots);
}

void Signal::connect(Slot *slot)
{
    slot->connect(this);
    m_connectedSlots->push_back(slot);
    m_connectedSlots->sort(slotPriorityComparisonLess);
}

void Signal::disconnect(Slot *slot)
{
    auto it = m_connectedSlots->begin();
    while (it != m_connectedSlots->end())
    {
        if (*it == slot)
        {
            (*it)->disconnect(this);
            it = m_connectedSlots->erase(it);
        }
        else
        {
            ++ it;
        }
    }
}

void Signal::run(void *param)
{
    auto it = m_connectedSlots->begin();
    while (it != m_connectedSlots->end())
    {
        if ((*it)->run(param))
        {
            //run succesfully and slot need repeat
            ++ it;
        }
        else
        {
            //failed in run or slot need no repeat, disconnect it
            (*it)->disconnect(this);
            it = m_connectedSlots->erase(it);
        }
    }
}

/*------------------------------------------------*/
SignalSet::SignalSet()
{
    m_signalMap = new std::map<int, Signal*>();
}

SignalSet::~SignalSet()
{
    for (auto pair: *m_signalMap)
    {
        delete pair.second;
    }
    m_signalMap->clear();
    TSP_SAFE_DELETE(m_signalMap);
}

Signal* SignalSet::getSignal(int event_id)
{
    Signal* ret = nullptr;
    auto it = m_signalMap->find(event_id);
    if (it == m_signalMap->end())
    {
        ret = new Signal();
        m_signalMap->emplace(event_id, ret);
    }
    else
    {
        ret = it->second;
    }
    return ret;
}

void SignalSet::connectSignal(int event_id, Slot *slot)
{
    this->getSignal(event_id)->connect(slot);
}

Slot* SignalSet::connectSignal(int event_id, const SLOT_FUNCTION& slot_function, int priority, bool repeat)
{
    Slot* slot = Slot::createSlot(slot_function, priority, repeat);
    this->getSignal(event_id)->connect(slot);
    return slot;
}

void SignalSet::emit(int event_id, void *param)
{
    this->getSignal(event_id)->run(param);
}

TSP_NS_END
